package cn.uway.utils;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author yiranli
 *
 */
public class WebUtils {
	
	/**
	 * IP校验正则串
	 */
	public static final String IP_REGEX="^((([1-9]|[1-9][0-9]|1\\d\\d|2[0-4]\\d|25[0-5])))\\.((([0-9]|[1-9][0-9]|1\\d\\d|2[0-4]\\d|25[0-5])))\\.((([0-9]|[1-9][0-9]|1\\d\\d|2[0-4]\\d|25[0-5]))|\\*)\\.((([0-9]|[1-9][0-9]|1\\d\\d|2[0-4]\\d|25[0-5]))|\\*)$";
	
	
	
	/**
	 * 将ServletRequest内getParameterMap方法返回的{@code Map<String, String[]>}对象
	 * 转换为{@code Map<String, Object>}对象.
	 * 
	 * @param request
	 * @return Map<String, Object><br>
	 * 			1. 若request.getParameterMap.size == 0, 则返回{@code Map<String, Object>}<br>
	 * 			2. 若原有value内String[] == null, 则将其变为null<br>
	 * 			3. 若原有value内String[].length == 0, 则将其变为""<br>
	 * 			4. 若原有value内String[].length == 1, 则将其变为string[0]<br>
	 * 			5. 若原有value内String[].length != 1, 则保留其为string[]<br>
	 * 
	 */
	public static Map<String, Object> parseParameterMap(HttpServletRequest request) {
		Map<String, String[]> paraMap = request.getParameterMap();
		Map<String, Object> rslt = new HashMap<String, Object>();
		
		if(paraMap == null || paraMap.size() == 0) {
			return rslt;
		}
		
		for( Entry<String, String[]> entry : paraMap.entrySet() ) {
			String key = entry.getKey();
			String[] value = entry.getValue();
			
			if( value == null ) {
				rslt.put(key, value);
			} else if( value.length == 0 ) {
				rslt.put(key, "");
			} else if( value.length == 1 ) {
				rslt.put(key, value[0]);
			} else {
				rslt.put(key, value);
			}
		}
		
		return rslt;
	}
	
	
	/**
	 * 创建UUID
	 */
	public static String createUUID(){
//		UUID uuid  =  UUID.randomUUID(); 
		String s = UUID.randomUUID().toString();
		String temp = s.substring(0, 8) + s.substring(9, 13) + s.substring(14, 18) + s.substring(19, 23) + s.substring(24);
		return temp;
	}
	
	/**
	 * 创建各表的主键ID
	 * @param tableName
	 * @return id<br/>
	 *     依照获取的表名拼装主键ID为：“2-5位表名简称”+yyyyMMddhhmmss+"5位随机数"
	 */
	public static String createPrimaryId(String tableName){
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmss");
		String dateTime = sdf.format(date);
		String rd = getRandomStr(5);
		return tableName+dateTime+rd;
	}
	
	/**
	 * 获取制定位数的随机字符串
	 * @param length
	 * @return String
	 */
	public static String getRandomStr(int length){
		String str="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";    
        StringBuffer sb = new StringBuffer();  
        Random random = new Random();
        for(int i = 0 ; i < length; ++i){  
            int number = random.nextInt(62);//[0,62)  
            sb.append(str.charAt(number));  
        }  
        return sb.toString(); 
	}
	
    /** 
     * 将一个 JavaBean 对象转化为一个  Map 
     * @param bean 要转化的JavaBean 对象 
     * @return 转化出来的  Map 对象 
     * @throws IntrospectionException 如果分析类属性失败 
     * @throws IllegalAccessException 如果实例化 JavaBean 失败 
     * @throws InvocationTargetException 如果调用属性的 setter 方法失败 
     */  
    
    @SuppressWarnings({ "rawtypes", "unchecked" })
	public static Map convertBean(Object bean)  
            throws IntrospectionException, IllegalAccessException, InvocationTargetException {  
        Class type = bean.getClass();  
        Map returnMap = new HashMap();  
        BeanInfo beanInfo = Introspector.getBeanInfo(type);  
  
        PropertyDescriptor[] propertyDescriptors =  beanInfo.getPropertyDescriptors();  
        for (int i = 0; i< propertyDescriptors.length; i++) {  
            PropertyDescriptor descriptor = propertyDescriptors[i];  
            String propertyName = descriptor.getName();  
            if (!propertyName.equals("class")) {  
                Method readMethod = descriptor.getReadMethod();  
                Object result = readMethod.invoke(bean, new Object[0]);  
                if (result != null) {  
                    returnMap.put(propertyName, result);  
                } else {  
                    returnMap.put(propertyName, "");  
                }  
            }  
        }  
        return returnMap;  
    }
    
    /** 
     * 将一个 Map 对象转化为一个 JavaBean 
     * @param type 要转化的类型 
     * @param map 包含属性值的 map 
     * @return 转化出来的 JavaBean 对象 
     * @throws IntrospectionException 如果分析类属性失败 
     * @throws IllegalAccessException 如果实例化 JavaBean 失败 
     * @throws InstantiationException 如果实例化 JavaBean 失败 
     * @throws InvocationTargetException 如果调用属性的 setter 方法失败 
     */  
    @SuppressWarnings("rawtypes")  
    public static Object convertMap(Class type, Map map)  
            throws IntrospectionException, IllegalAccessException,  
            InstantiationException, InvocationTargetException {  
        BeanInfo beanInfo = Introspector.getBeanInfo(type); // 获取类属性  
        Object obj = type.newInstance(); // 创建 JavaBean 对象  
  
        // 给 JavaBean 对象的属性赋值  
        PropertyDescriptor[] propertyDescriptors =  beanInfo.getPropertyDescriptors();  
        for (int i = 0; i< propertyDescriptors.length; i++) {  
            PropertyDescriptor descriptor = propertyDescriptors[i];  
            String propertyName = descriptor.getName();  
  
            if (map.containsKey(propertyName)) {  
                // 下面一句可以 try 起来，这样当一个属性赋值失败的时候就不会影响其他属性赋值。  
                Object value = map.get(propertyName);  
  
                Object[] args = new Object[1];  
                args[0] = value;  
  
                descriptor.getWriteMethod().invoke(obj, args);  
            }  
        }  
        return obj;  
    }
    
    /**
	 * 解析前台传递参数并获取参数列表<br/>
	 * HttpServletRequest.getParameterMap的方法返回map的rawtypes 为
	 * {@code Map<String, String[]>}, 不能为MyBatis所直接使用;<br/>
	 * 该方法用于处理该Map形成{@code Map<String, Object>}进行返回;
	 * 
	 * @param req
	 *            页面请求;
	 * @return 页面参数列表Map;<br/>
	 *         若前台页面参数map的size为0或者空, 则返回空Map;<br/>
	 *         若value数组的长度为1, 则将其转换为string[0];<br/>
	 *         若value数组的长度为0, 则将其转换为""; <br/>
	 *         若value数组的长度为其它, 则保持数组形式不变;
	 */
	public static Map<String, Object> parseWebParas(HttpServletRequest req) {
		Map<String, String[]> paras = req.getParameterMap();
		Map<String, Object> rslt = new HashMap<String, Object>();

		if (paras == null || paras.size() == 0) {
			return rslt;
		}

		for (Entry<String, String[]> entry : paras.entrySet()) {
			String key = entry.getKey();
			String[] values = entry.getValue();

			if (values != null && values.length == 1) {
				rslt.put(key, values[0]);
			} else if (values != null && values.length == 0) {
				rslt.put(key, "");
			} else {
				rslt.put(key, values);
			}
		}

		return rslt;
	}
	
	public static double[] xyzToGCJ(double[] r){
		double[] pos = new double[3];
		double FE_WGS84 = 1.0/298.257223563;
		double RE_WGS84  =  6378137.0;
		double PI=3.1415926535897932;
		double e2=FE_WGS84*(2.0-FE_WGS84);
		
		double r2=dot(r,r,2);
		double z = 0.0;
		double zk = 0.0;
		double v = RE_WGS84;
		for(z=r[2],zk=0.0;Math.abs(z-zk)>=1E-4;){
			zk=z;
			double sinp=z/Math.sqrt(r2+z*z);
			v = RE_WGS84/Math.sqrt(1.0-e2*sinp*sinp);
			z=r[2]+v*e2*sinp;
		}
		pos[0]=r2>1E-12?Math.atan(z/Math.sqrt(r2)):(r[2]>0.0?PI/2.0:-PI/2.0);
	    pos[1]=r2>1E-12?Math.atan2(r[1],r[0]):0.0;
	    pos[2]=Math.sqrt(r2+z*z)-v;
	    
	    pos[0] = pos[0]*180/PI;
	    pos[1] = pos[1]*180/PI;
		return pos;
	}
	private static double dot(double[] r1,double[] r2,int i){
		double c = 0.0;
		while(--i>=0){
			c += r1[i] * r2[i];
		}
		return c;
	}
	
	public static double[] xyzToGCJ2(double[] r){
		double[] pos = new double[3];
		double FE_WGS84 = 1.0/298.257223563;
		double RE_WGS84  =  6378137.0;
		double PI=3.1415926535897932;
		double e2=FE_WGS84*(2.0-FE_WGS84);
		
		double r2=dot(r,r,2);
		double z = 0.0;
		double zk = 0.0;
		double v = RE_WGS84;
		for(z=r[2],zk=0.0;Math.abs(z-zk)>=1E-4;){
			zk=z;
			double sinp=z/Math.sqrt(r2+z*z);
			v = RE_WGS84/Math.sqrt(1.0-e2*sinp*sinp);
			z=r[2]+v*e2*sinp;
		}
		pos[0]=r2>1E-12?Math.atan(z/Math.sqrt(r2)):(r[2]>0.0?PI/2.0:-PI/2.0);
	    pos[1]=r2>1E-12?Math.atan2(r[1],r[0]):0.0;
	    pos[2]=Math.sqrt(r2+z*z)-v;
	    
//	    pos[0] = pos[0]*180/PI;
//	    pos[1] = pos[1]*180/PI;
		return pos;
	}
	
	public static double[] XYZ2ENU(double[] r,double[] rb){
		double[] eum = new double[3];
		double[] pos = new double[3];
		double[] r_rb = new double[3];
		r_rb[0] = r[0]-rb[0];
		r_rb[1] = r[1]-rb[1];
		r_rb[2] = r[2]-rb[2];
		pos = xyzToGCJ(rb);
		eum = ecef2enu(pos,r_rb);
		return eum;
		
	}
	
	public static double[] ecef2enu(double[] pos,double[] r_rb){
		double[] E = new double[9];
		double sinp=Math.sin(pos[0]),cosp=Math.cos(pos[0]),sinl=Math.sin(pos[1]),cosl=Math.cos(pos[1]);
	    
	    E[0]=-sinl;      E[3]=cosl;       E[6]=0.0;
	    E[1]=-sinp*cosl; E[4]=-sinp*sinl; E[7]=cosp;
	    E[2]=cosp*cosl;  E[5]=cosp*sinl;  E[8]=sinp;
	    
	    String[] tr = new String[3];
	    tr[0] = "N";
	    tr[1] = "N";
		return matmul(tr,3,1,3,1.0,E,r_rb,0.0);
	}
	
	public static double[] matmul(String[] tr, int n, int k, int m, double alpha,
            double[] A, double[] B, double beta){
		double d;
	    int i,j,x,f=tr[0].equals("N")?(tr[1].equals("N")?1:2):(tr[1].equals("N")?3:4);  /*NN NT TT TN*/
	    double[] C = new double[3];
		//程序中矩阵是按列存储的，但是对矩阵做乘法的时候仍然是相同的法则.
	    for (i=0;i<n;i++) for (j=0;j<k;j++) 
		{
	        d=0.0;
	        switch (f) {
	            case 1: for (x=0;x<m;x++) d+=A[i+x*n]*B[x+j*m]; break; /*NN*/
	            case 2: for (x=0;x<m;x++) d+=A[i+x*n]*B[j+x*k]; break; /*NT*/
	            case 3: for (x=0;x<m;x++) d+=A[x+i*m]*B[x+j*m]; break; /*TN*/
	            case 4: for (x=0;x<m;x++) d+=A[x+i*m]*B[j+x*k]; break; /*TT*/
	        }
	        if (beta==0.0) 
	        	C[i+j*n]=alpha*d; 
	        else 
	        	C[i+j*n]=alpha*d+beta*C[i+j*n];
	    }
		return C;
		
	}
	
	public static double enuToAngle(double x,double y){
		return Math.abs(Math.toDegrees(Math.atan(y/x)));
	}
	
	public  static boolean isEmpty(String str){
		return str==null || "".equals(str.trim());
	}
	
	public  static boolean isNotEmpty(String str){
		return str!=null && !"".equals(str.trim());
	}
	
	
	/**
	 * @description:获取请求ip(注意不能异步使用) 
	 * @param:
	 * @Date:2016年5月3日上午9:18:43
	 * @author xiaoyong
	 */
	public static String getRemoteIp(HttpServletRequest request) {
		String ip=request.getHeader("x-forwarded-for");
		
		if(ip==null || "".equals(ip.trim()) || "unknown".equalsIgnoreCase(ip.trim())) {
			ip=request.getHeader("Proxy-Client-IP");
		}else{
			ip=ip.trim().replaceAll("\\s", "");
			
			String[] ipList=ip.split(",");
			
			for(int i=0;i<ipList.length;i++){
				if(!ipList[i].equalsIgnoreCase("unknown") && ipList[i].matches(IP_REGEX)) {
					ip=ipList[i];
					break;
				}
			}
		}
		
		if(ip==null || "".equals(ip.trim()) || "unknown".equalsIgnoreCase(ip.trim())) {
			ip=request.getHeader("WL-Proxy-Client-IP");
		}
		
		if(ip==null || "".equals(ip.trim()) || "unknown".equalsIgnoreCase(ip.trim())) {
			ip=request.getRemoteAddr();
		}
		
		return ip;
	}
	
	
	/**
	 * @description:判断是否get请求
	 * @author pengcc
	 */
	public static boolean isGet(HttpServletRequest request){
		return "GET".equalsIgnoreCase(request.getMethod());
	}
	
	/**
	 * @description:判断是否微信浏览器
	 * @author pengcc
	 */
	public static boolean isWeixinBrowser(HttpServletRequest request){
		String userAgent=request.getHeader("user-agent");
		if(userAgent!=null && userAgent.toLowerCase().indexOf("micromessenger")>0){
			return true;
		}
		return false;
	}
	
	/**
	 * @description:是否ajax请求 
	 * @param:
	 * @author pengcc
	 */
	public static boolean isAjax(HttpServletRequest request){
		return request.getHeader("x-requested-with")!=null && request.getHeader("x-requested-with").equalsIgnoreCase("XMLHttpRequest");
	}
	
	public static void writeToPage(String content, HttpServletResponse response) {
		response.setCharacterEncoding("UTF-8");
		response.setHeader("Content-Type", "text/javascript;charset=UTF-8");
		response.setHeader("Pragma", "no-cache");  
		response.setHeader("Cache-Control", "no-cache");  
		response.setDateHeader("Expires", 0); 		
		PrintWriter pw = null;		
		try {
			pw = response.getWriter();
			pw.write(content);
		} 
		catch (Exception e) {
			e.printStackTrace();
		} 
		finally {
			if (pw != null) {
				pw.flush();
				pw.close();
			}
		}
	}  	 
	
	
}
